﻿/* Copyright 2010 Igor Francé
 * 
 * This file is part of ECMADoc (http://ecmadoc.codeplex.com).
 * 
 * ECMADoc is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * ECMADoc is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with ECMADoc. 
 * If not, see http://www.gnu.org/licenses/.
 */
namespace EcmaDoc.Core.Configuration
{
	using System;
	using System.Text.RegularExpressions;

	using EcmaDoc.Core.Items;

	/// <summary>
	/// Defines the delegate that handles token matches in the documentation.
	/// </summary>
	/// <param name="item">The code item on which the token has been added.</param>
	/// <param name="match">The token match instance that contains information about the current match.</param>
	public delegate void TokenHandler(CodeItem item, TokenMatch match);

	/// <summary>
	/// Defines the delegate that handles the parser matches.
	/// </summary>
	/// <param name="context">The current parse context under which this match was found.</param>
	/// <remarks>
	/// Match handlers drive the parsing process, parsing and structuring the found matches within the supplied item.
	/// </remarks>
	internal delegate void MatchHandler(ParseContext context);

	/// <summary>
	/// Contains class that contains the match and token handlers provides by <c>ECMADoc</c>.
	/// </summary>
	public static class Handlers
	{
		private static readonly Regex dotExpr = new Regex(@"\.");

		/// <summary>
		/// Match handler for function statements.
		/// </summary>
		/// <param name="context">The current parse context under which this match was found.</param>
		/// <remarks>
		/// <para>
		/// Before parsing a javascript source file, the files are being pre-processed. During the pre-processing, the 
		/// functions and comments will be recursively removed and stored in parser registry and replaced 
		/// with placeholders.
		/// </para>
		/// <para>
		/// Therefore, it is important to keep in mind that the actual code that will be matched will
		/// not contain the body of the comment or the function texts. It will instead contain the placeholders that contain
		/// their indexes which can be used to query the parser for their actual bodies. For instance, the function that was
		/// specified like this in the source code:
		/// </para>
		/// <code>
		/// /**
		///  * Returns the specified value multiplied by itself.
		///  * @param {Number} x The value to square.
		///  * @returns {Number} The squared &lt;c&gt;x&lt;/c&gt;.
		///  */
		/// function square(x)
		/// { 
		///   return Math.pow(x, 2);
		/// }
		/// </code>
		/// <para>
		/// will at the time the parsing is being done look like this:
		/// </para>
		/// <code>
		/// [comment45]
		/// function square(x)
		/// [function64]
		/// </code>
		/// <para>
		/// The full bodies can then be retrieved using the <see cref="Parser.GetComment(int)">GetComment</see> and 
		/// <see cref="Parser.GetFunction(int)">GetFunction</see> methods of the <see cref="Parser"/>.
		/// </para>
		/// <para>
		/// The regular expression is defined in the <see cref="ParserConfig">configuration file</see>, and it needs to
		/// have the following match groups:</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Name</term>
		/// <description>Remarks</description>
		/// </listheader>
		/// <item>
		/// <term>CommentIndex</term>
		/// <description>The index of the comment text of the matched function, if any. To get the body of the 
		/// comment with that index, use <see cref="Parser.GetComment(int)"/>.</description>
		/// </item>
		/// <item>
		/// <term>VarDecl</term>
		/// <description>The <c>var</c> keyword, if any, preceding the left-hand assignment statement precing the function
		/// declaration; for instance: <c><b>var</b> x = function () { ... }</c></description>
		/// </item>
		/// <item>
		/// <term>NewKeyword</term>
		/// <description>The <c>new</c> keyword, if any, preceding the left-hand assignment statement preceding the function
		/// declaration; for instance: <c>var x = <b>new</b> Function();</c></description>
		/// </item>
		/// <item>
		/// <term>SurroundingBracket</term>
		/// <description>The <c>(</c> bracket symbol, if any, preceding the right side of the function declaration; 
		/// for instance: <c>var x = <b>(</b>function executeInline() { ... })()</c>. This helps identify inline functions
		/// that should be treated as private</description>
		/// </item>
		/// <item>
		/// <term>FunctArgs</term>
		/// <description>The function arguments text; for instance: <c>function setProperty(<b>object, property</b>) { ... }</c></description>
		/// </item>
		/// <item>
		/// <term>FunctIndex</term>
		/// <description>The index of the matched function. To get the body of the function with that index, use 
		/// <see cref="Parser.GetFunction(int)"/>
		/// </description>
		/// </item>
		/// <item>
		/// <term>FunctDeclLeft</term>
		/// <description>The left side of the function declaration, for instance: <c><b>Square.prototype.width</b> = function () { ... }</c></description>
		/// </item>
		/// <item>
		/// <term>FunctDeclRight</term>
		/// <description>The right side of the function declaration, for instance: <c>Square.prototype.width = function <b>Square$width</b>() { ... }</c></description>
		/// </item>
		/// <item>
		/// <term>FunctCall</term>
		/// <description>Contains the paranthetical, if any, that was applied to the result of the function expression; for instance:
		/// <c>(function () { ... })<b>(ns)</b></c></description>
		/// </item>
		/// <item>
		/// <term>FunctCallArgs</term>
		/// <description>Contains the arguments of the paranthetical, if any, that was applied to the result of the function 
		/// expression; for instance: <c>(function () { ... })(<b>ns</b>)</c></description>
		/// </item>
		/// </list>
		/// </remarks>
		[MatchHandler("FunctionHandler")] 
		internal static void FunctionHandler(ParseContext context)
		{
			GroupCollection g = context.Match.Groups;
			string comment = g["CommentIndex"].Value;
			string varDecl = g["VarDecl"].Value;
			string newKeyword = g["NewKeyword"].Value;
			string functArgs = g["FunctArgs"].Value;
			string functIndex = g["FunctIndex"].Value;
			string functDeclLeft = g["FunctDeclLeft"].Value;
			string functDeclRight = g["FunctDeclRight"].Value;
			string functCall = g["FunctCall"].Value;
			string functCallArgs = g["FunctCallArgs"].Value;
			string surroundingBracket = g["SurroundingBracket"].Value;

			bool isAnonymous = !string.IsNullOrEmpty(surroundingBracket);
			if (functDeclLeft == string.Empty && functDeclRight == string.Empty)
			{
				functDeclRight = ParserConfig.AnonymousName;
				isAnonymous = true;
			}

			string functName = functDeclLeft.Length != 0 ? functDeclLeft : functDeclRight;
			string functBody = context.Parser.GetFunction(functIndex);

			bool isPrivate = varDecl.Length != 0 || !string.IsNullOrEmpty(surroundingBracket);
			bool isLiteral = context.Mode == ParseMode.Literal;
			bool isGlobal = context.Mode == ParseMode.Global;

			bool isExecutedInline = newKeyword.Length != 0 || functCall.Length != 0;

			bool isLeftEmpty = functDeclLeft.Length == 0;
			bool isRightEmpty = functDeclRight.Length == 0;

			bool hasDotsInName = dotExpr.IsMatch(functDeclLeft);

			if (!string.IsNullOrEmpty(functCall) && string.IsNullOrEmpty(newKeyword))
				isPrivate = true;
			else if (!isGlobal && !isLiteral && (isAnonymous || !hasDotsInName))
				isPrivate = true; 
			
			MatchFlags flags = MatchFlags.None;
			if (isPrivate)
				flags |= MatchFlags.Private;
			if (isLiteral)
				flags |= MatchFlags.Literal;

			CodeItem parentItem = context.Object;

			if (isAnonymous && isPrivate && string.IsNullOrEmpty(comment))
				return;

			if (isAnonymous)
			{
				if (!string.IsNullOrEmpty(functBody))
				{
					CodeItem tempItem = new FunctionItem(parentItem, "temp");
					context.Parser.ParseString(functBody, tempItem, context.Mode);

					foreach (CodeItem item in tempItem.Members)
						parentItem.AddMember(item.Name, Visibility.Private, item);
				}
			}
			else
			{
				var function = new FunctionItem(parentItem, functName, context.Parser.GetComment(comment), functArgs);
				if (!string.IsNullOrEmpty(functBody))
					context.Parser.ParseString(functBody, function, function.IsConstructor 
						? ParseMode.Constructor 
						: ParseMode.Function);

				CodeItem member;
				if (isExecutedInline)
				{
					member = function.ReturnItem ?? 
						new ObjectItem(parentItem, functName, context.Parser.GetComment(comment));
				}
				else
					member = function;

				if (functDeclLeft.Length != 0)
					context.Parser.AddMember(parentItem, member, functDeclLeft, flags);

				if ((isLeftEmpty && !isRightEmpty))
					context.Parser.AddMember(parentItem, member, functDeclRight, flags);
			}
		}

		[MatchHandler("ReturnHandler")]
		internal static void ReturnHandler(ParseContext context)
		{
			GroupCollection g = context.Match.Groups;
			string result = g["Result"].Value.Trim();

			var function = context.Object as FunctionItem;
			if (function != null)
				function.ReturnItem = context.Object.GetMember(result);
		}

		/// <summary>
		/// Match handler for namespace-like expressions.
		/// </summary>
		/// <param name="context">The current parse context under which this match was found.</param>
		/// <remarks>
		/// <para>
		/// Typically, a namespace-like expression could look like this:
		/// </para>
		/// <code>
		/// /**
		///  * Contains various shapes that can be used in drawing.
		///  */
		/// Type.registerNamespace("drawing.shapes");
		/// </code>
		/// <para>which, of course, when pre-processed would look like this:</para>
		/// <code>
		/// [comment12]
		/// Type.registerNamespace([string45]);
		/// </code>
		/// <para>
		/// The regular expression is defined in the <see cref="ParserConfig">configuration file</see>, and it needs to
		/// have the following match groups:</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Name</term>
		/// <description>Remarks</description>
		/// </listheader>
		/// <item>
		/// <term>StringIndex</term>
		/// <description>The index of the string that contains the name of the matched namespace. To get the body of the 
		/// string with that index, use <see cref="Parser.GetString(int)"/>.</description>
		/// </item>
		/// <item>
		/// <term>CommentIndex</term>
		/// <description>The index of the comment text of the matched namespace, if any. To get the body of the 
		/// comment with that index, use <see cref="Parser.GetComment(int)"/>.</description>
		/// </item>
		/// </list>
		/// </remarks>
		[MatchHandler("NamespaceHandler")]
		internal static void NamespaceHandler(ParseContext context)
		{
			GroupCollection g = context.Match.Groups;
			string ns = context.Parser.GetString(g["StringIndex"].Value);
			string comment = context.Parser.GetString(g["CommentIndex"].Value);

			DocumentItem document = context.Object.OwnerDocument;
			if (document != null)
				document.AddNamespace(ns, comment);
		}

		/// <summary>
		/// Match handler for generic JavaScript expressions.
		/// </summary>
		/// <param name="context">The current parse context under which this match was found.</param>
		/// <remarks>
		/// <para>
		/// Typically, a generic expression could look like this:
		/// </para>
		/// <code>
		/// /**
		///  * Defines some value.
		///  * @type {String}
		///  */
		/// var x = "My value";
		/// </code>
		/// <para>which, of course, when pre-processed would look like this:</para>
		/// <code>
		/// [comment47]
		/// var x = [string12];
		/// </code>
		/// <para>
		/// The regular expression is defined in the <see cref="ParserConfig">configuration file</see>, and it needs to
		/// have the following match groups:</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Name</term>
		/// <description>Remarks</description>
		/// </listheader>
		/// <item>
		/// <term>CommentIndex</term>
		/// <description>The index of the comment text of the matched statement, if any. To get the body of the 
		/// comment with that index, use <see cref="Parser.GetComment(int)"/>.</description>
		/// </item>
		/// <item>
		/// <term>VarDecl</term>
		/// <description>The <c>var</c> keyword, if any; for instance: <c><b>var</b> x = 45;</c></description>
		/// </item>
		/// <item>
		/// <term>VarValue</term>
		/// <description>The value of the variable being assigned, if any; for instance: 
		/// <c>var x = <b>45</b>;</c></description>
		/// </item>
		/// <item>
		/// <term>VarName</term>
		/// <description>The name of the variable being assigned; for instance: <c>var <b>x</b> = 45;</c></description>
		/// </item>
		/// </list>
		/// </remarks>
		[MatchHandler("GenericHandler")]
		internal static void GenericHandler(ParseContext context)
		{
			GroupCollection g = context.Match.Groups;
			
			string comment = g["CommentIndex"].Value;
			string varDecl = g["VarDecl"].Value;
			string varValue = g["VarValue"].Value;
			string varName = g["VarName"].Value;

			bool isPrivate = varDecl != string.Empty && context.Mode != ParseMode.Global;

			CodeItem parentItem = context.Object;

			MatchFlags flags = MatchFlags.None;
			if (isPrivate)
				flags |= MatchFlags.Private;
			if (context.Mode == ParseMode.Literal)
				flags |= MatchFlags.Literal;
			if (varName.IndexOf(".") != -1)
				flags |= MatchFlags.PropertyAssignment;

			CodeItem member = new ObjectItem(parentItem, varName, context.Parser.GetComment(comment));
			context.Parser.AddMember(parentItem, member, varName, flags);
		}

		/// <summary>
		/// Match handler for object literal (JSON) JavaScript statements.
		/// </summary>
		/// <param name="context">The current parse context under which this match was found.</param>
		/// <remarks>
		/// <para>
		/// Typically, a literal expression could look like this:
		/// </para>
		/// <code>
		/// /**
		///  * Defines some values.
		///  */
		/// var x = { 
		///   /**
		///    * Defines the first number
		///    * @type {Number}
		///    */
		///   prop1: 45,
		///   /**
		///    * Defines the second number
		///    * @type {Array}
		///    */
		///   prop2: [1, 2, 3]
		/// };
		/// </code>
		/// <para>which, of course, when pre-processed would look like this:</para>
		/// <code>
		/// [comment32]
		/// var x = [literal23];
		/// </code>
		/// <para>
		/// The regular expression is defined in the <see cref="ParserConfig">configuration file</see>, and it needs to
		/// have the following match groups:</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Name</term>
		/// <description>Remarks</description>
		/// </listheader>
		/// <item>
		/// <term>CommentIndex</term>
		/// <description>The index of the comment text of the matched statement, if any. To get the body of the 
		/// comment with that index, use <see cref="Parser.GetComment(int)"/>.</description>
		/// </item>
		/// <item>
		/// <term>VarDecl</term>
		/// <description>The <c>var</c> keyword, if any; for instance: <c><b>var</b> x = { p: 45 };</c></description>
		/// </item>
		/// <item>
		/// <term>VarName</term>
		/// <description>The name of the variable being assigned; for instance: <c>var <b>x</b> = { p: 45 };</c></description>
		/// </item>
		/// <item>
		/// <term>LiteralIndex</term>
		/// <description>The index of the literal that got matched. To get the body of the 
		/// literal with that index, use <see cref="Parser.GetLiteral(int)"/>.</description>
		/// </item>
		/// <item>
		/// <term>Prototype</term>
		/// <description>The prototype keyword, if any; for instance: 
		/// <c>Square.<b>prototype</b>.x = { p: 45 };</c></description>
		/// </item>
		/// </list>
		/// </remarks>
		[MatchHandler("LiteralHandler")]
		internal static void LiteralHandler(ParseContext context)
		{
			GroupCollection g = context.Match.Groups;
			string comment = g["CommentIndex"].Value;

			string varDecl = g["VarDecl"].Value;
			string varName = g["VarName"].Value;
			string literalIndex = g["LiteralIndex"].Value;
			bool isPrototype = g["Prototype"].Value.Length != 0;

			string objectBody = context.Parser.GetLiteral(literalIndex);

			CodeItem parentItem = context.Object;
			CodeItem member = new ObjectItem(parentItem, varName, context.Parser.GetComment(comment));

			MatchFlags flags = MatchFlags.Literal;
			if (isPrototype)
				flags |= MatchFlags.Prototype;

			if (context.Mode != ParseMode.Literal)
				parentItem = context.Parser.GetOwnerItem(parentItem, varName);

			member = context.Parser.AddMember(parentItem, member, varName, flags);
			context.Parser.ParseString(objectBody, member, ParseMode.Literal);

			if (isPrototype)
			{
				member.MoveMembers(Visibility.Instance, Visibility.Prototype);
			}
		}

		/// <summary>
		/// Match handler for object creation statements.
		/// </summary>
		/// <param name="context">The current parse context under which this match was found.</param>
		/// <remarks>
		/// <para>
		/// Typically, an object creation statement expression could look like this:
		/// </para>
		/// <code>
		/// /**
		///  * Creates a new date instance
		///  * @type {Date}
		///  */
		/// var x = new Date(1285944532294);
		/// </code>
		/// <para>which, of course, when pre-processed would look like this:</para>
		/// <code>
		/// [comment32]
		/// var x = new Date(1285944532294);
		/// </code>
		/// <para>
		/// The regular expression is defined in the <see cref="ParserConfig">configuration file</see>, and it needs to
		/// have the following match groups:</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Name</term>
		/// <description>Remarks</description>
		/// </listheader>
		/// <item>
		/// <term>CommentIndex</term>
		/// <description>The index of the comment text of the matched statement, if any. To get the body of the 
		/// comment with that index, use <see cref="Parser.GetComment(int)"/>.</description>
		/// </item>
		/// <item>
		/// <term>VarDecl</term>
		/// <description>The <c>var</c> keyword, if any; for instance: <c><b>var</b> x = new Date(1285944532294);</c></description>
		/// </item>
		/// <item>
		/// <term>VarName</term>
		/// <description>The name of the variable being assigned; for instance: <c>var <b>x</b> = new Date(1285944532294);</c></description>
		/// </item>
		/// <item>
		/// <term>VarValue</term>
		/// <description>The value of the variable being assigned, if any; for instance: 
		/// <c>var x = new <b>Date(1285944532294)</b>;</c></description>
		/// </item>
		/// </list>
		/// </remarks>
		[MatchHandler("NewStatementHandler")]
		internal static void NewStatementHandler(ParseContext context)
		{
			GroupCollection g = context.Match.Groups;

			// string ctorArgs = g["CtorArgs"].Value;
			string comment = g["CommentIndex"].Value;
			string varDecl = g["VarDecl"].Value;
			string varName = g["VarName"].Value;
			string varValue = g["VarValue"].Value;
			bool isPrivate = varDecl != string.Empty;

			CodeItem parentItem = context.Object;

			var member = new ObjectItem(parentItem, varName, context.Parser.GetComment(comment));
			member.Type = varValue;

			MatchFlags flags = MatchFlags.None;
			if (isPrivate)
				flags |= MatchFlags.Private;

			context.Parser.AddMember(context.Object, member, varName, flags);
		}

		/// <summary>
		/// Match handler for class inheritance statements.
		/// </summary>
		/// <param name="context">The current parse context under which this match was found.</param>
		/// <remarks>
		/// <para>
		/// Typically, a class inheritance expression in JavaScript would look like this:
		/// </para>
		/// <code>
		/// Square.prototype = new Shape();
		/// </code>
		/// <para>
		/// The regular expression is defined in the <see cref="ParserConfig">configuration file</see>, and it needs to
		/// have the following match groups:</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Name</term>
		/// <description>Remarks</description>
		/// </listheader>
		/// <item>
		/// <term>VarName</term>
		/// <description>The name of the class being extended; for instance: <c><b>Square</b>.prototype = new Shape();</c></description>
		/// </item>
		/// <item>
		/// <term>VarValue</term>
		/// <description>The name of the parent class; for instance: 
		/// <c>Square.prototype = new <b>Shape</b>();</c></description>
		/// </item>
		/// <item>
		/// <term>CtorArgs</term>
		/// <description>Constructor arguments, if any, that were supplied the parent class; for instance: 
		/// <c>Square.prototype = new Shape(<b>456</b>);</c></description>
		/// </item>
		/// </list>
		/// </remarks>
		[MatchHandler("InheritanceStatement")]
		internal static void InheritanceStatementHandler(ParseContext context)
		{
			GroupCollection g = context.Match.Groups;

			string varName = g["VarName"].Value;
			string varValue = context.Parser.GetValue(g["VarValue"].Value);

			//// string ctorArgs = g["CtorArgs"].Value;

			var member = context.Parser.GetItem(context.Object, varName);
			member.AddBaseType(varValue);
		}

		/// <summary>
		/// Match handler for object property statements.
		/// </summary>
		/// <param name="context">The current parse context under which this match was found.</param>
		/// <remarks>
		/// <para>
		/// Typically, an object property expression in JavaScript would look like this:
		/// </para>
		/// <code>
		/// /**
		///  * Specifies some property.
		///  * @type {Shape}
		///  */
		/// this.prop1 = new Shape();
		/// </code>
		/// <para>which, of course, when pre-processed would look like this:</para>
		/// <code>
		/// [comment32]
		/// this.prop1 = new Shape();
		/// </code>
		/// <para>
		/// The regular expression is defined in the <see cref="ParserConfig">configuration file</see>, and it needs to
		/// have the following match groups:</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Name</term>
		/// <description>Remarks</description>
		/// </listheader>
		/// <item>
		/// <term>CommentIndex</term>
		/// <description>The index of the comment text of the matched statement, if any. To get the body of the 
		/// comment with that index, use <see cref="Parser.GetComment(int)"/>.</description>
		/// </item>
		/// <item>
		/// <term>PropDecl</term>
		/// <description>The name of the property being created; for instance: <c>this.<b>prop1</b> = new Shape();</c></description>
		/// </item>
		/// <item>
		/// <term>PropValue</term>
		/// <description>The value of the property being created; for instance: <c>this.prop1 = <b>new Shape()</b>;</c></description>
		/// </item>
		/// </list>
		/// </remarks>
		[MatchHandler("ObjectPropHandler")]
		internal static void ObjectPropHandler(ParseContext context)
		{
			GroupCollection g = context.Match.Groups;
			string comment = g["CommentIndex"].Value;
			string propDecl = g["PropDecl"].Value;
			string propValue = g["PropValue"].Value;

			CodeItem parentItem = context.Object;
			CodeItem member = new ObjectItem(parentItem, propDecl, context.Parser.GetComment(comment));

			context.Parser.AddMember(parentItem, member, propDecl, MatchFlags.None);
		}

		/// <summary>
		/// Adds a property to the specified <paramref name="item"/>, as specified by the matched token.
		/// </summary>
		/// <param name="item">The current parse context under which this match was found.</param>
		/// <param name="match">The token match being handled.</param>
		[TokenHandler("AddProperty")]
		internal static void AddProperty(CodeItem item, TokenMatch match)
		{
			item.AddProperty(new ItemProperty(match.Name, match.Data[match.Name], match.Data));
		}

		/// <summary>
		/// Adds a function argument to the specified <paramref name="item"/>, as specified by the matched token.
		/// </summary>
		/// <param name="item">The current parse context under which this match was found.</param>
		/// <param name="match">The token match being handled.</param>
		[TokenHandler("AddArgument")]
		internal static void AddArgument(CodeItem item, TokenMatch match)
		{
			if (!(item is FunctionItem))
			{
				Log.WriteLine(LogCategory.Error,
					"AddArgument handler called on a non-function context. Context is {0}. Data is: {1}",
					item.ToString(), match.Value);

				return;
			}

			FunctionItem function = (FunctionItem) item;

			string name = match.Data["Name"];
			string type = match.Data["Type"];
			string description = match.Data["Description"];

			Argument arg;
			if (function.DocumentedArguments.ContainsKey(name))
			{
				arg = function.DocumentedArguments[name];
			}
			else
			{
				arg = new Argument(name);
				function.DocumentedArguments.Add(name, arg);
			}
			
			arg.Type = type;
			arg.Description = description;
		}

		/// <summary>
		/// Adds function arguments to the specified <paramref name="item"/>, as specified by the matched token.
		/// </summary>
		/// <param name="item">The current parse context under which this match was found.</param>
		/// <param name="match">The token match being handled.</param>
		[TokenHandler("AddArguments")]
		internal static void AddArguments(CodeItem item, TokenMatch match)
		{
			if (!(item is FunctionItem))
			{
				Log.WriteLine(LogCategory.Error,
					"AddArgument handler called on a non-function context. Context is {0}. Data is: {1}",
					item.ToString(), match.Value);

				return;
			}
			FunctionItem function = (FunctionItem) item;

			string type = match.Data["Type"];
			string range = match.Data["Range"];
			string description = match.Data["Description"];

			function.SetVariableArguments(type, range, description);
		}

		/// <summary>
		/// Adds a namespace to the specified document <paramref name="item"/>, as specified by the matched token.
		/// </summary>
		/// <param name="item">The current parse context under which this match was found.</param>
		/// <param name="match">The token match being handled.</param>
		[TokenHandler("AddNamespace")]
		internal static void AddNamespace(CodeItem item, TokenMatch match)
		{
			if (item is DocumentItem)
			{
				string name = match.Data["namespace"];
				string description = match.Data["description"];
				if (!string.IsNullOrEmpty(name))
					((DocumentItem) item).AddNamespace(name, description);
			}
		}

		/// <summary>
		/// Adds an event to the specified <paramref name="item"/>, as specified by the matched token.
		/// </summary>
		/// <param name="item">The current parse context under which this match was found.</param>
		/// <param name="match">The token match being handled.</param>
		[TokenHandler("AddEvent")]
		internal static void AddEvent(CodeItem item, TokenMatch match)
		{
			string name = match.Data["name"];
			string summary = match.Data["summary"];
			string description = match.Data["description"];

			CodeItem evt = new ObjectItem(item, name);
			evt.ItemType = ItemType.Event;
			evt.Comment = Comment.Create(evt);
			evt.Comment.Summary = summary;
			evt.Comment.Description = description;

			item.AddMember("event_" + name, Visibility.Prototype, evt);
		}

		/// <summary>
		/// Sets the function return value specification, as specified by the matched token.
		/// </summary>
		/// <param name="item">The current parse context under which this match was found.</param>
		/// <param name="match">The token match being handled.</param>
		[TokenHandler("SetReturns")]
		internal static void SetReturns(CodeItem item, TokenMatch match)
		{
			if (item is FunctionItem)
				((FunctionItem) item).Returns = new ReturnItem(match.Data["type"], match.Data["description"]);
		}

		/// <summary>
		/// Sets the type of the specified <paramref name="item"/>, as specified by the matched token.
		/// </summary>
		/// <param name="item">The current parse context under which this match was found.</param>
		/// <param name="match">The token match being handled.</param>
		[TokenHandler("SetType")]
		internal static void SetType(CodeItem item, TokenMatch match)
		{
			item.Type = match.Data["type"];
		}

		/// <summary>
		/// Sets the visibility of the specified <paramref name="item"/>, as specified by the matched token.
		/// </summary>
		/// <param name="item">The current parse context under which this match was found.</param>
		/// <param name="match">The token match being handled.</param>
		[TokenHandler("SetVisibility")]
		internal static void SetVisibility(CodeItem item, TokenMatch match)
		{
			if (string.IsNullOrEmpty(match.Data["value"]))
				return;

			Visibility visibility;
			Enum.TryParse(match.Data["value"], out visibility);
			item.Visibility = visibility;
		}

		/// <summary>
		/// Sets the context type of the specified <paramref name="item"/>, as specified by the matched token.
		/// </summary>
		/// <param name="item">The current parse context under which this match was found.</param>
		/// <param name="match">The token match being handled.</param>
		[TokenHandler("SetContextType")]
		internal static void SetContextType(CodeItem item, TokenMatch match)
		{
			if (string.IsNullOrEmpty(match.Data["value"]))
				return;

			ItemType type;
			Enum.TryParse(match.Data["value"], out type);
			item.ItemType = type;
		}

		/// <summary>
		/// Sets the boolean indicating that the the specified function <paramref name="item"/> is a constructor.
		/// </summary>
		/// <param name="item">The current parse context under which this match was found.</param>
		/// <param name="match">The token match being handled.</param>
		[TokenHandler("SetIsConstructor")]
		internal static void SetIsConstructor(CodeItem item, TokenMatch match)
		{
			item.ItemType = ItemType.Class;
			item.IsConstructor = true;
		}
	}
}